package cn.murky.admin.system.biz.endpoint;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.murky.admin.core.utils.SecurityUtils;
import cn.murky.admin.system.biz.domain.bo.SysNoticeReadBO;
import cn.murky.admin.system.biz.domain.dto.UserNoticePageDTO;
import cn.murky.admin.system.biz.domain.entity.SysNotice;
import cn.murky.admin.system.biz.domain.vo.UserNoticePageVO;
import cn.murky.admin.system.biz.service.ISysNoticeReadService;
import cn.murky.admin.system.biz.service.ISysNoticeService;
import cn.murky.common.utils.CollectionUtils;
import cn.murky.common.utils.StringUtils;
import cn.murky.security.entity.SecurityUserInfo;
import cn.murky.socketd.SdResult;
import com.mybatisflex.core.paginate.Page;
import lombok.extern.slf4j.Slf4j;
import org.noear.snack.ONode;
import org.noear.socketd.transport.core.Message;
import org.noear.socketd.transport.core.Session;
import org.noear.socketd.transport.core.listener.EventListener;
import org.noear.solon.annotation.Inject;
import org.noear.solon.net.annotation.ServerEndpoint;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static cn.murky.socketd.constants.SocetdConstants.ORIGIN_COLUMN;

@ServerEndpoint("/Notice")
@Slf4j
public class NoticeEndpoint extends EventListener {
    private static final String TOKEN_COLUMN = "token";
    private static final String GET_EVENT = "/get";
    private static final String PUSH_EVENT = "/push";
    private static final String READ_EVENT = "/read";
    private static final String UNREAD_EVENT = "/unread";
    List<Session> userSessions = new ArrayList<>();
    @Inject
    private ISysNoticeService iSysNoticeService;
    @Inject
    private ISysNoticeReadService iSysNoticeReadService;



    /**
     * 读取事件
     * message所需传递参数: UserNoticePageDTO
     */
    private void doOnGet(Session session, Message message) throws IOException {
        UserNoticePageDTO dto = ONode.deserialize(message.dataAsString(), UserNoticePageDTO.class);
        String token = getToken(session);
        SecurityUserInfo userInfo = SecurityUtils.getUserInfo(token);
        try {
            Page<UserNoticePageVO> userNoticePageVOPage = SecurityUtils.callable(userInfo, () -> iSysNoticeService.getEffectiveList(dto));
            SdResult.replyEndOk(userNoticePageVOPage,session,message);
        } catch (Exception e) {
            e.fillInStackTrace();
            SdResult.replyEndFail(session,message,e.getMessage());
        }
    }

    /**
     * 读取事件
     * message所需传递参数: List<Long>
     */
    private void doOnRead(Session session, Message message) {
        List<Long> noticeIdList = ONode.deserialize(message.dataAsString(), (new ArrayList<Long>() {
        }).getClass());
        if (CollectionUtils.isNotEmpty(noticeIdList)) {
            String token = getToken(session);
            SecurityUserInfo userInfo = SecurityUtils.getUserInfo(token);
            SysNoticeReadBO sysNoticeReadBO = new SysNoticeReadBO().setFkUserId(userInfo.getUserId()).setFkNoticeIds(noticeIdList);
            SecurityUtils.runnable(userInfo, () -> {
                boolean read = iSysNoticeReadService.read(sysNoticeReadBO);
                try {
                    if (read) {
                        SdResult.replyEndOk(noticeIdList, session, message);
                    } else {
                        SdResult.replyEndFail(session, message);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    /**
     * 读取事件
     * message所需传递参数: List<Long>
     */
    private void doOnUnread(Session session, Message message) {
        List<Long> noticeIdList = ONode.deserialize(message.dataAsString(), (new ArrayList<Long>() {
        }).getClass());
        if (CollectionUtils.isNotEmpty(noticeIdList)) {
            String token = getToken(session);
            SecurityUserInfo userInfo = SecurityUtils.getUserInfo(token);
            SysNoticeReadBO sysNoticeReadBO = new SysNoticeReadBO().setFkNoticeIds(noticeIdList);
            SecurityUtils.runnable(userInfo, () -> {
                boolean b = iSysNoticeReadService.unread(sysNoticeReadBO);
                try {
                    if (b) {
                        SdResult.replyEndOk(session, message);
                    } else {
                        SdResult.replyEndFail(session, message);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    public NoticeEndpoint() {
        doOnOpen(s -> {
            // 连接的合法性鉴定
            Map<String, String> paramMap = s.handshake().paramMap();
            String token = paramMap.get(TOKEN_COLUMN);
            if (StringUtils.isEmpty(token)) {
                log.info("[NoticeEndpoint]----->doOnOpen---->非法的连接{}", paramMap.get(ORIGIN_COLUMN));
                s.close();
            }
            SaSession saSession = StpUtil.getTokenSessionByToken(token);
            if (saSession != null && StringUtils.isNotEmpty(saSession.getId())) {
                userSessions.add(s);
                return;
            }
            log.info("[NoticeEndpoint]----->doOnOpen---->非法的token连接{}", paramMap.get(ORIGIN_COLUMN));
            s.close();
        });
        doOnClose(s -> {
            log.debug("[NoticeEndpoint]----->doOnClose---->{}断开连接", s.sessionId());
            userSessions.remove(s);
        });
        // 挂载事件
        doOn(GET_EVENT, this::doOnGet);
        doOn(READ_EVENT, this::doOnRead);
        doOn(UNREAD_EVENT, this::doOnUnread);
    }

    /**
     * 获取当前用户的token
     *
     * @return token
     */
    private String getToken(Session session) {
        Map<String, String> paramMap = session.handshake().paramMap();
        return paramMap.get(TOKEN_COLUMN);
    }

    /**
     * 发送系统通知到管理端
     */
    public void sendSysNotice(SysNotice sysNotice) {
        for (Session session : userSessions) {
            try {
                SdResult.send(sysNotice, session, PUSH_EVENT);
            } catch (IOException e) {
                e.fillInStackTrace();
            }
        }
    }

}